/*
 * Anything: A flexible and easy to use data structure that combines the 
 * characteristics of a list and a hashmap into one data structure.
 * 
 * Based on a paper by Peter Sommerlad and Marcel Rueedi on 
 * 'Do-it-yourself reflection' entered at EuroPLOP 1998.
 * 
 * This library was written completely from scratch and is based only 
 * in spirit on the original Java Anything implementation written by 
 * Andre Weinand/Marcel Rueedi in 1997/99 and enhanced by Stefan Tramm 
 * in 2006.
 * 
 * Copyright (c) 2008 Thomas Marti and others. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package any.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;

import any.Any;
import any.AnyVisitor;
import any.ImmutableAny;
import any.functor.interfaces.Closure;
import any.functor.interfaces.Function;
import any.functor.interfaces.Predicate;
import any.impl.util.SafeSimpleDateFormat;
import any.incubator.IterableConfig;


public abstract class SimpleTypeAnything<V> extends BaseAnything {

	private static final long serialVersionUID = 1L;

	//TODO: Move somewhere else? (SequentialAny, Immutable, AnyUtil)
	protected static final DateFormat[] ANY_DATE_FORMATS = new DateFormat[] {
		new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S"),
		new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"),
		new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm"),
		new SafeSimpleDateFormat("yyyy-MM-dd")
	};

	protected final V contents;

	protected final Class<?> type;

	@SuppressWarnings("unchecked")
	protected SimpleTypeAnything(V contents) {
		this(contents, (Class<? super V>)contents.getClass());
	}
	
	SimpleTypeAnything(V contents, Class<? super V> type) {
		this.contents = contents;
		this.type = type;
	}
	
	@Override
	public Any clone() {
		// Since most Any implementations have an immutable 'contents' 
		// object we call toAny() here. Subclasses that need cloning must 
		// overwrite this method.
		return toAny();
	}
	
	public String asString(String defaultValue) {
		return contents.toString();
	}
	
	@Override
	public String toString() {
		return asString(null);
	}
	
	public String toFullString() {
		return getType().getSimpleName() + "\"" + contents + "\"";
	}

	@SuppressWarnings("unchecked")
	public <T> T toObject() {
		return (T)contents;
	}
	
	public <T> T toNative() {
		// For most Any implementations toObject() returns an 
		// object of their native type. Subclasses that need 
		// other behaviour must overwrite this method.
		return toObject();
	}
	
	public final Class<?> getType() {
		return type;
	}

	public final boolean isSimpleType() {
		return true;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null || obj.getClass() != this.getClass()) return false;
		
		SimpleTypeAnything<?> any = (SimpleTypeAnything<?>)obj;

		return contents.equals(any.contents);
	}

	@Override
	public int hashCode() {
		return contents.hashCode();
	}
	
	// Helpers
	
	/**
	 * TODO: Doc!
	 * @param <T>
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	<T> T tryClone(T value) {
		if (value instanceof Cloneable) {
			try {
				return (T)value.getClass().getMethod("clone").invoke(value);
			} catch (Exception e) {}
		}
		
		if (value instanceof Serializable) {
			try {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				new ObjectOutputStream(out).writeObject(value);
				return (T)new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())).readObject();
			} catch (RuntimeException e) {} catch (IOException e) {} catch (ClassNotFoundException e) {}
		}
		
		return value;
	}
	
	// Not implemented here
	public final boolean onlySequentialAccess() {
		throw new AssertionError("Should never be reached!");
	}
	public final Entry<String, Any> getEntry(int index) throws IndexOutOfBoundsException {
		throw new AssertionError("Should never be reached!");
	}
	@SuppressWarnings("unchecked")
	public final Object give(Function func) throws NullPointerException {
		throw new AssertionError("Should never be reached!");
	}
	@SuppressWarnings("unchecked")
	public final boolean accept(AnyVisitor visitor) {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean matches(Predicate<? super Entry<String, ? extends ImmutableAny>> predicate) {
		throw new AssertionError("Should never be reached!");		
	}
	public final Iterable<? extends Entry<String, Any>> entries() {
		throw new AssertionError("Should never be reached!");
	}
	public final Iterable<? extends Entry<String, Any>> entries(IterableConfig config) {
		throw new AssertionError("Should never be reached!");
	}
	public final Iterator<ImmutableAny> iterator() {
		throw new AssertionError("Should never be reached!");
	}
	public final Iterable<Any> iterable() {
		throw new AssertionError("Should never be reached!");
	}
	public final Iterable<Any> iterable(IterableConfig config) {
		throw new AssertionError("Should never be reached!");
	}
	public final ListIterator<Any> listIterator() {
		throw new AssertionError("Should never be reached!");
	}
	public final ListIterator<Any> listIterator(int index) throws IndexOutOfBoundsException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any toSynchronized() {
		throw new AssertionError("Should never be reached!");
	}
	public final Any toAny(boolean mutable) {
		throw new AssertionError("Should never be reached!");
	}
	public final Any toMutable() {
		throw new AssertionError("Should never be reached!");
	}
	public final ImmutableAny toImmutable() {
		throw new AssertionError("Should never be reached!");
	}
	public final Any append(Object value) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any appendAll(Object values) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any appendEach(Object... values) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any clone(boolean mutable) {
		throw new AssertionError("Should never be reached!");
	}
	public final Any execute(Closure<? super Any> func) throws NullPointerException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any getPut(String key, Object defaultValue) throws NullPointerException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any getPut(String key) throws NullPointerException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any getSet(int index, Object defaultValue) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any getSet(int index) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean hasIndex(int index) {
		throw new AssertionError("Should never be reached!");
	}	
	public final Any insert(int index, Object value) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any insert(int index, String key, Object value) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any insertAll(int index, Object values) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any insertEach(int index, Object... values) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean isEmpty() {
		throw new AssertionError("Should never be reached!");
	}
	public final Any put(Entry<?, ?> entry) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any put(String key, Object value) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any putAll(Iterable<? extends Entry<?, ?>> entries) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any putAll(Map<?, ?> map) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Entry<String, Any> remove(int index) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Entry<String, Any> remove(String key) throws NullPointerException, IllegalStateException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Entry<String, Any>[] removeElementsWhere(Predicate<? super Entry<String, ? extends ImmutableAny>> predicate) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Entry<String, Any> removeFirstElementWhere(Predicate<? super Entry<String, ? extends ImmutableAny>> predicate) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any removeKey(String key) throws NullPointerException, IllegalStateException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any replaceKey(String oldKey, String newKey) throws NullPointerException, IllegalStateException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any replaceValue(Object newValue) throws UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any set(int index, Object value) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any set(int index, String key, Object value) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any setAll(int index, Object values) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any setEach(int index, Object... values) throws IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any setKey(String key, int index) throws NullPointerException, IndexOutOfBoundsException, UnsupportedOperationException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any get(int index) throws IndexOutOfBoundsException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any get(int index, Object defaultValue) throws IndexOutOfBoundsException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any get(String key) throws NullPointerException, IllegalStateException {
		throw new AssertionError("Should never be reached!");
	}
	public final Any get(String key, Object defaultValue) throws NullPointerException {
		throw new AssertionError("Should never be reached!");
	}
	public final <T> List<Entry<String, T>> asEntriesList() {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean containsKey(String key) throws NullPointerException {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean containsKeys() {
		throw new AssertionError("Should never be reached!");
	}
	public final int getIndexForKey(String key) throws NullPointerException {
		throw new AssertionError("Should never be reached!");
	}
	public final int[] getIndicesForValue(Object value) {
		throw new AssertionError("Should never be reached!");
	}
	public final int[] getIndicesWhere(Predicate<? super Entry<String, ? extends ImmutableAny>> predicate) {
		throw new AssertionError("Should never be reached!");
	}
	public final String getKeyForIndex(int index) {
		throw new AssertionError("Should never be reached!");
	}
	public final String[] getKeysWhere(Predicate<? super Entry<String, ? extends ImmutableAny>> predicate) {
		throw new AssertionError("Should never be reached!");
	}
	public final String[] getKeysForValue(Object value) {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean isMutable() {
		throw new AssertionError("Should never be reached!");
	}
	public final boolean isStale() {
		throw new AssertionError("Should never be reached!");
	}
	public final List<String> keys() {
		throw new AssertionError("Should never be reached!");
	}
	public final int size() {
		throw new AssertionError("Should never be reached!");
	}
}
